
package org.apache.solr.search;

/**
 * <code>DocSlice</code> implements DocList as an array of docids and optional scores.
 *
 *
 * @since solr 0.9
 */
public class DocSlice extends DocSetBase implements DocList {

    final int offset;    // starting position of the docs (zero based)
    final int len;       // number of positions used in arrays
    final int[] docs;    // a slice of documents (docs 0-100 of the query)
    final float[] scores;  // optional score list
    final int matches;
    final float maxScore;

    /**
     * Primary constructor for a DocSlice instance.
     *
     * @param offset starting offset for this range of docs
     * @param len length of results
     * @param docs array of docids starting at position 0
     * @param scores array of scores that corresponds to docs, may be null
     * @param matches total number of matches for the query
     */
    public DocSlice(int offset, int len, int[] docs, float[] scores, int matches, float maxScore) {
        this.offset = offset;
        this.len = len;
        this.docs = docs;
        this.scores = scores;
        this.matches = matches;
        this.maxScore = maxScore;
    }

    @Override
    public DocList subset(int offset, int len) {

        if (this.offset == offset && this.len == len) {
            return this;
        }

        // if we didn't store enough (and there was more to store)
        // then we can't take a subset.
        int requestedEnd = offset + len;
        if (requestedEnd > docs.length && this.matches > docs.length) {
            return null;
        }
        int realEndDoc = Math.min(requestedEnd, docs.length);
        int realLen = Math.max(realEndDoc - offset, 0);
        if (this.offset == offset && this.len == realLen) {
            return this;
        }
        return new DocSlice(offset, realLen, docs, scores, matches, maxScore);
    }

    @Override
    public boolean hasScores() {
        return scores != null;
    }

    @Override
    public float maxScore() {
        return maxScore;
    }

    @Override
    public int offset() {
        return offset;
    }

    @Override
    public int size() {
        return len;
    }

    @Override
    public int matches() {
        return matches;
    }

    @Override
    public long memSize() {
        return (docs.length << 2)
                + (scores == null ? 0 : (scores.length << 2))
                + 24;
    }

    @Override
    public boolean exists(int doc) {
        int end = offset + len;
        for (int i = offset; i < end; i++) {
            if (docs[i] == doc) {
                return true;
            }
        }
        return false;
    }

    // Hmmm, maybe I could have reused the scorer interface here...
    // except that it carries Similarity baggage...
    @Override
    public DocIterator iterator() {
        return new DocIterator() {
            int pos = offset;
            final int end = offset + len;

            @Override
            public boolean hasNext() {
                return pos < end;
            }

            @Override
            public Integer next() {
                return nextDoc();
            }

            /**
             * The remove operation is not supported by this Iterator.
             */
            @Override
            public void remove() {
                throw new UnsupportedOperationException("The remove  operation is not supported by this Iterator.");
            }

            @Override
            public int nextDoc() {
                return docs[pos++];
            }

            @Override
            public float score() {
                return scores[pos - 1];
            }
        };
    }

    @Override
    public DocSet intersection(DocSet other) {
        if (other instanceof SortedIntDocSet || other instanceof HashDocSet) {
            return other.intersection(this);
        }
        HashDocSet h = new HashDocSet(docs, offset, len);
        return h.intersection(other);
    }

    @Override
    public int intersectionSize(DocSet other) {
        if (other instanceof SortedIntDocSet || other instanceof HashDocSet) {
            return other.intersectionSize(this);
        }
        HashDocSet h = new HashDocSet(docs, offset, len);
        return h.intersectionSize(other);
    }

    @Override
    public boolean intersects(DocSet other) {
        if (other instanceof SortedIntDocSet || other instanceof HashDocSet) {
            return other.intersects(this);
        }
        HashDocSet h = new HashDocSet(docs, offset, len);
        return h.intersects(other);
    }

    @Override
    protected DocSlice clone() {

        try {
            // DocSlice is not currently mutable
            DocSlice slice = (DocSlice) super.clone();
        }
        catch (CloneNotSupportedException e) { }

        return null;
    }
}
